#ifndef CIRCULARBUFFERTEMPLATE_H_INCLUDED
#define CIRCULARBUFFERTEMPLATE_H_INCLUDED
////////////////////////////////////
// Circular Buffer type template
// author lxs lukecian@163.com
// Create 2018.01.8
////////////////////////////////////
#include <stdio.h>

/*
* Error codes define
*/
#define CBUF_EMPTY -3
#define CBUF_FULL  -2
#define CBUF_ERROR -1
#define CBUF_OK     0

#define CIRCULAR_BUF_DECALARE(T) \
typedef struct _cbuf_##T{\
	T* buf;                \
    unsigned int read_pos;          \
	unsigned int write_pos;         \
	unsigned int capacity; \
	unsigned int size;	   \
	int (*compare)(T *obj1,T *obj2);\
} circular_buffer_##T;\
\
void circular_buffer_init_##T(circular_buffer_##T *thiz, \
                              int (*compare)(T *obj1,T *obj2),\
                              T* buf_ptr,unsigned int capacity); \
\
void circular_buffer_reset_##T(circular_buffer_##T *thiz); \
\
int circular_buffer_put_##T(circular_buffer_##T *thiz, T *obj);\
\
int circular_buffer_get_##T(circular_buffer_##T *thiz, T *obj);\
\
int circular_buffer_write_##T(circular_buffer_##T *thiz, T *buf,unsigned int len);\
\
int circular_buffer_read_##T(circular_buffer_##T *thiz, T *buf,unsigned int len);\
\
int circular_buffer_indexof_##T(circular_buffer_##T *thiz,T *objs,unsigned int len);


#define CIRCULAR_BUF_DEFINE(T,name) \
        circular_buffer_##T name

#define CIRCULAR_BUF_TYPE(T) \
         circular_buffer_##T

/**! This is code template generation! **/
#define CIRCULAR_BUF_GENERATE(T)\
void  \
circular_buffer_init_##T(circular_buffer_##T *thiz,\
                         int (*compare)(T *obj1,T *obj2),\
                         T* buf_ptr,unsigned int capacity) \
{\
	thiz->capacity = capacity;\
    thiz->size = 0; \
    thiz->read_pos = 0; \
    thiz->write_pos = 0; \
    thiz->buf = buf_ptr; \
    thiz->compare = compare;\
    printf("%s cbuf init ok...\n",#T);\
}\
\
\
void circular_buffer_reset_##T(circular_buffer_##T *thiz)\
{\
    thiz->size = 0;\
    thiz->read_pos = 0;\
    thiz->write_pos = 0;\
}\
\
\
int \
circular_buffer_put_##T(circular_buffer_##T *thiz, T* obj)\
{\
    if (thiz->size == thiz->capacity)\
        return CBUF_FULL;\
    printf("cbuf of %s put...\n",#T);\
    thiz->buf[thiz->write_pos++] = *obj;\
    if (thiz->write_pos >= thiz->capacity){\
        thiz->write_pos = 0;\
    }\
    thiz->size += 1;\
    return CBUF_OK;\
}\
\
\
int \
circular_buffer_get_##T(circular_buffer_##T *thiz, T* obj)\
{\
    printf("%s(),cbuf of %s get...\n",__FUNCTION__,#T);\
    if (thiz->size == 0)\
        return CBUF_EMPTY;\
    \
    *obj = thiz->buf[thiz->read_pos++];\
    if (thiz->read_pos >= thiz->capacity){\
        thiz->read_pos = 0;\
    }\
    return CBUF_OK;\
}\
\
\
int circular_buffer_write_##T(circular_buffer_##T *thiz, T *buf,unsigned int len)\
{\
\
    if (thiz->size == thiz->capacity)\
        return CBUF_FULL;\
\
	int writable_count = 0;\
\
	int space = thiz->capacity - thiz->size;\
\
	if(len > 0 && len >= space)\
		writable_count = space;\
	else if(len > 0 && len < space)\
		writable_count = len;\
	else\
        return CBUF_ERROR;\
    int i;\
    for (i = 0; i < writable_count; i++)\
    {\
        thiz->buf[thiz->write_pos++] = buf[i];\
        if (thiz->write_pos >= thiz->capacity){\
            thiz->write_pos = 0;\
        }\
    }\
    thiz->size += writable_count;\
    return CBUF_OK;\
}\
\
int circular_buffer_read_##T(circular_buffer_##T *thiz, T *buf,unsigned int len)\
{\
\
    if (thiz->size == 0)\
        return CBUF_EMPTY;\
\
    int readable_count = 0;\
 \
    if(len > 0 && len <= thiz->size)\
        readable_count = len;\
    else if(len > 0 && len > thiz->size)\
        readable_count = thiz->size;\
    else\
        return CBUF_ERROR;\
\
    int i;\
    for ( i = 0; i < readable_count; i++)\
    {\
        buf[i] = thiz->buf[thiz->read_pos++];\
        if (thiz->read_pos >= thiz->capacity){\
            thiz->read_pos = 0;\
        }\
    }\
    thiz->size -= readable_count;\
    return CBUF_OK;\
}\
\
int circular_buffer_indexof_##T(circular_buffer_##T *thiz,\
                                 T *obj,unsigned int len)\
{\
    int cnt = thiz->size;\
    int pos = thiz->read_pos;\
    if(!thiz->compare) return CBUF_ERROR;\
\
    while (cnt-- > 0)\
    {\
        int index = pos;\
        int i;\
        for(i=0;i < len; i++){\
            if (thiz->compare(&thiz->buf[(index + i) % thiz->capacity] ,&obj[i])!=0){\
                break;\
			}\
            if(i == (len - 1)){\
                thiz->read_pos = index;\
                return pos;\
            }\
		}\
		\
        pos++;\
        if(pos >= thiz->capacity){\
            pos = 0;\
        }\
    }\
    return CBUF_ERROR;\
}\

#define CIRCULAR_BUF_INIT(instance,type,compare,buf_ptr,capacity) \
            circular_buffer_init_##type(instance,compare,buf_ptr,capacity);

#define CIRCULAR_BUF_RESET(instance,type) \
           circular_buffer_reset_##type(instance);

#define CIRCULAR_BUF_PUT(instance,type,obj) \
           circular_buffer_put_##type(instance,obj);

#define CIRCULAR_BUF_GET(instance,type,obj) \
           circular_buffer_get_##type(instance,obj);

#define CIRCULAR_BUF_READ(instance,type,obj,len) \
           circular_buffer_read_##type(instance,obj,len);

#define CIRCULAR_BUF_WRITE(instance,type,obj,len) \
           circular_buffer_write_##type(instance,obj,len);

#define CIRCULAR_BUF_INDEX_OF(instance,type,obj,len) \
           circular_buffer_indexof_##type(instance,obj,len);

#define CIRCULAR_BUF_CREATE(T) \
              CIRCULAR_BUF_DECALARE(T) \
              CIRCULAR_BUF_GENERATE(T)
#endif // CIRCULARBUFFERTEMPLATE_H_INCLUDED
